/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.general.config;

import com.alibaba.fastjson.JSON;
import org.apache.commons.io.IOUtils;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;

/**
 * {@link ServletRequest} {@link ConfigSource}
 *
 * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
 * @since 1.0.0
 */
public class ServletRequestConfigSource implements ConfigSource {

    private final ServletRequest servletRequest;

    private final String name;

    private final int ORDINAL_NUMBER = 600;

    private final Map<String, Collection<Object>> configData;


    public ServletRequestConfigSource(ServletRequest servletRequest) {
        this.servletRequest = servletRequest;
        this.name = String.format("servletRequestConfigSource: [%s]", servletRequest.toString());
        this.configData = new HashMap<>();
    }

    @Override
    public int getOrdinal() {
        return ORDINAL_NUMBER;
    }

    @Override
    public Set<String> getPropertyNames() {
        return getConfigData().keySet();
    }

    /**
     * 返回collection的toString
     * @param s
     * @return
     */
    @Override
    public String getValue(String s) {
        return getConfigData().get(s).toString();
    }

    @Override
    public String getName() {
        return this.name;
    }

    protected Map<String, Collection<Object>> getConfigData() {
        try {
            if (configData.isEmpty()) {
                prepareConfigData(configData);
            }
        } catch (Throwable cause) {
            throw new IllegalStateException("准备配置数据发生错误", cause);
        }
        return configData;
    }

    private void prepareConfigData(Map<String, Collection<Object>> configData) {
        // 是一个HttpServletRequest
        if(HttpServletRequest.class.isAssignableFrom(servletRequest.getClass())){
            HttpServletRequest request = (HttpServletRequest)servletRequest;
            String method = request.getMethod();
            if(method.equalsIgnoreCase("GET")){
                parsePropertiesFromGetRequest(configData);
            }
            else if(method.equalsIgnoreCase("POST")){
                parsePropertiesFromPOSTRequest(request, configData);
            }
            else{
                throw new IllegalStateException("不支持GET和POST之外的请求方法!");
            }
        }
        else{
            throw new IllegalStateException("当前请求不是HttpServletRequest， 无法解析Method!");
        }

    }

    /**
     * 从post请求中获取参数值
     * @param request 请求
     * @param configData 配置数据
     */
    private void parsePropertiesFromPOSTRequest(HttpServletRequest request, Map<String, Collection<Object>> configData) {
        // 获取内容格式
        String contentType = request.getContentType();
        if (contentType != null && !contentType.equals("")) {
            contentType = contentType.split(";")[0];
        }

        // form表单格式  表单形式可以从 ParameterMap中获取
        if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) {
            // 获取参数
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (parameterMap != null) {
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    configData.put(entry.getKey(), Arrays.asList(entry.getValue()));
                }
            }
        }

        // json格式 json格式需要从request的输入流中解析获取
        if ("application/json".equalsIgnoreCase(contentType)) {
            // 使用 commons-io中 IOUtils 类快速获取输入流内容
            String paramJson = null;
            try {
                paramJson = IOUtils.toString(request.getInputStream(), "UTF-8");
                Map<String, Object> parseObject = JSON.parseObject(paramJson, Map.class);
                for(Map.Entry<String, Object> entry: parseObject.entrySet()){
                    if(entry.getValue() instanceof Collection){
                        configData.put(entry.getKey(), (Collection<Object>) entry.getValue());
                    }
                    else{
                        configData.put(entry.getKey(), Arrays.asList(entry.getValue()));
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException("json解析出错,e: "+e);
            }
        }
    }

    /**
     * 从GET请求中获取参数属性
     * @param configData 配置数据
     */
    private void parsePropertiesFromGetRequest(Map<String, Collection<Object>> configData) {
        Map<String, String[]> map = servletRequest.getParameterMap();
        if(!map.isEmpty()){
            for(Map.Entry<String, String[]> entry: map.entrySet()){
                String[] values = servletRequest.getParameterValues(entry.getKey());

                if (values == null || values.length == 0)
                {
                    continue;
                }
                else
                {
                    // ensure that escape characters in all list elements are removed
                    List<Object> result = new ArrayList<Object>(values.length);
                    for (int i = 0; i < values.length; i++)
                    {
                        Object val = handleDelimiters(values[i]);
                        if (val instanceof Collection)
                        {
                            result.addAll((Collection<?>) val);
                        }
                        else
                        {
                            result.add(val);
                        }
                    }
                    configData.put(entry.getKey(), result);
                }
            }
        }
    }

    /**
     * 处理List默认分隔符,
     * @param value 字符串
     * @return 一个String 或者一个Collection<String>
     */
    private Object handleDelimiters(String value) {
        String[] strings = StringUtils.delimitedListToStringArray(value, ",");
        List<String> stringList = Arrays.asList(strings);
        return stringList.size() == 1 ? stringList.get(0): stringList;
    }
}
